package net.guerlab.smart.wx.service.service.impl;

import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import net.guerlab.smart.wx.core.enums.WxAppType;
import net.guerlab.smart.wx.core.exception.WxAppInvalidException;
import net.guerlab.smart.wx.cp.spring.storage.WxCpRedisTemplateConfigStorage;
import net.guerlab.smart.wx.service.entity.WxApp;
import net.guerlab.smart.wx.service.service.AfterWxAppDeleteHandler;
import net.guerlab.smart.wx.service.service.AfterWxAppUpdateHandler;
import net.guerlab.smart.wx.service.service.WxAppService;
import net.guerlab.smart.wx.service.service.WxCpManagerService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 微信小程序服务管理实现
 *
 * @author guer
 */
@Service
public class WxCpManagerServiceImpl implements WxCpManagerService, AfterWxAppUpdateHandler, AfterWxAppDeleteHandler {

    private final Map<String, WxCpService> serviceMap = new ConcurrentHashMap<>(8);

    private final Lock initLock = new ReentrantLock();

    private WxAppService wxAppService;

    private RedisTemplate<String, String> redisTemplate;

    @Override
    public WxCpService getService(String appId) {
        appId = StringUtils.trimToNull(appId);
        if (appId == null) {
            return null;
        }

        WxCpService service = serviceMap.get(appId);

        if (service != null) {
            return service;
        }

        initLock.lock();
        try {
            WxCpConfigStorage config = buildConfigStorage(appId);
            service = new WxCpServiceImpl();
            service.setWxCpConfigStorage(config);

            serviceMap.put(appId, service);

            return service;
        } finally {
            initLock.unlock();
        }
    }

    private WxCpConfigStorage buildConfigStorage(String appId) {
        WxApp wxApp = wxAppService.selectById(appId);
        if (wxApp == null || wxApp.getWxAppType() != WxAppType.CP) {
            throw new WxAppInvalidException();
        }

        WxCpRedisTemplateConfigStorage storage = new WxCpRedisTemplateConfigStorage(redisTemplate);
        storage.setCorpId(wxApp.getAppId());
        storage.setAgentId(wxApp.getAgentId());
        storage.setCorpSecret(wxApp.getSecret());
        storage.setToken(wxApp.getToken());
        storage.setAesKey(wxApp.getAesKey());
        return storage;
    }

    @Override
    public void afterWxAppUpdateHandler(WxApp wxApp) {
        if (wxApp.getWxAppType() == WxAppType.CP) {
            afterWxAppDeleteHandler(wxApp.getAppId());
        }
    }

    @Override
    public void afterWxAppDeleteHandler(String appId) {
        initLock.lock();
        try {
            serviceMap.remove(appId);
        } finally {
            initLock.unlock();
        }
    }

    @Autowired
    public void setWxAppService(WxAppService wxAppService) {
        this.wxAppService = wxAppService;
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

}
